package com.xian.im.modules.mis;

import com.xian.im.common.properties.LimitProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;


/**
 * @program: xian-im-server
 * @description:  process
 * @author: liru.xian
 * @create: 2021-02-05 10:06
 **/
@Slf4j
@Component
public class LimitProcess {
    public static final String KEY = "xim:limit:ip:";
    private static final Long MINUTE = 60*1000L;
    private static final Long HOUR = 60*1000*60L;
    private static final Long DAY = 60*1000*60*24L;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private LimitProperties limitProperties;

    /**
     * 使能过滤器
     * @param ipAddress
     * @return
     */
    public boolean dealRequest(String ipAddress) {
        //获取当前时间
        long currentTimeMillis = System.currentTimeMillis();
        long aMinuteAgo = currentTimeMillis - MINUTE;
        long aHourAgo = currentTimeMillis - HOUR;
        long aDayAgo = currentTimeMillis - DAY;
        double datCount = getZSetCount(KEY+ipAddress,aDayAgo,currentTimeMillis);
        if(datCount >limitProperties.getDaySize()){
            log.warn("触发每天限流策略 {}",ipAddress);
            return false;
        }
        double hourCount = getZSetCount(KEY+ipAddress,aHourAgo,currentTimeMillis);
        if(hourCount >limitProperties.getHourSize()){
            log.warn("触发每小时限流策略 {}",ipAddress);
            return false;
        }
        double minuteCount = getZSetCount(KEY+ipAddress,aMinuteAgo,currentTimeMillis);
        if(minuteCount >limitProperties.getMinuteSize()){
            log.warn("触发每分钟限流策略 {}",ipAddress);
            return false;
        }
        addZSet(KEY+ipAddress, currentTimeMillis, currentTimeMillis+"");
        return true;
    }

    public boolean dayRequest(String mark,Long count){
        long currentTimeMillis = System.currentTimeMillis();
        long aDayAgo = currentTimeMillis - DAY;
        //如果条件成立  说明 一天之内的请求次数已经越界了
        if(getZSetCount(KEY+mark,aDayAgo,currentTimeMillis)>count){
            log.warn("触发每分钟{} 次限流策略 {}",count,mark);
            return false;
        }
        addZSet(KEY+mark, currentTimeMillis, currentTimeMillis+"");
        return true;
    }

    public boolean hourRequest(String mark,Long count){
        long currentTimeMillis = System.currentTimeMillis();
        long aHourAgo = currentTimeMillis - HOUR;
        //如果条件成立  说明 一小时之内的请求次数已经越界了
        if(getZSetCount(KEY+mark,aHourAgo,currentTimeMillis)>count){
            log.warn("触发每分钟{} 次限流策略 {}",count,mark);
            return false;
        }
        addZSet(KEY+mark, currentTimeMillis, currentTimeMillis+"");
        return true;
    }

    /**
     * 使能过滤器
     * @param mark
     * @return
     */
    public boolean minuteRequest(String mark,Long count) {
        long currentTimeMillis = System.currentTimeMillis();
        long aMinuteAgo = currentTimeMillis - MINUTE;
        if(getZSetCount(KEY+mark,aMinuteAgo,currentTimeMillis)>count){
            log.warn("触发每分钟{} 次限流策略 {}",count,mark);
            return false;
        }
        addZSet(KEY+mark, currentTimeMillis, currentTimeMillis+"");
        return true;
    }

    /**
     * 添加进Zset
     * @param key
     * @param score
     * @param member
     */
    private void addZSet(String key, double score, String member) {
        // 集合名字key 存放的时间member  随机值score
        redisTemplate.opsForZSet().add(key,member,score);
    }

    /**
     * 添加进Zset
     * @param key
     * @param min
     * @param max
     * @return
     */
    private double getZSetCount(String key, double min, double max) {
        // 集合名字key 开始区间min  结束区间max
        return redisTemplate.opsForZSet().count(key,min,max);
    }
}
